<?php


namespace app\service\alone\Upload\Traits;


use think\facade\Cache;
use think\facade\Filesystem;
use think\file\UploadedFile;

trait FragmentationTrait
{
    /**
     * 存储文件
     *
     * @param UploadedFile $file
     * @param string $fileName
     * @return array
     */
    private function storage(UploadedFile $file, string $fileName): array
    {
        $fileBlock = fopen($this->data['storage']['dir'] . $this->data['storage']['temp'] . $fileName, 'w+');

        fwrite($fileBlock, file_get_contents($file->getRealPath()));

        fclose($fileBlock);

        return echoArr(200, '请求成功');
    }

    /**
     * 加入文件队列
     *
     * @param string $fileName
     */
    private function pushFileQueue(string $fileName): void
    {
        $file = fopen($this->data['lock']['queue'], 'a+');
        flock($file, LOCK_EX);
        fwrite($file, $fileName . "\n");
        flock($file, LOCK_UN);
        fclose($file);
    }

    /**
     * 验证文件是否需合并
     *
     * @param string $key
     *
     * @return bool
     */
    private function verifyIsMergeFile(string $key): bool
    {
        // 文件队列
        $queueList = $this->getFileQueue();

        // 全部文件上传成功则合并
        if (count($queueList) == $this->data['info']['totalSheet']) {
            // 是否合并成功
            if ($this->mergeFile($queueList)) {
                // 删除文件块
                $this->deleteFileBlockRecording($queueList, $key);

                return true;
            }
        }

        return false;
    }

    /**
     * 获取已上传的文件列表
     *
     * @return array
     */
    private function getFileQueue(): array
    {
        // 读取文件
        $file = fopen($this->data['lock']['queue'], 'r');
        $text = fread($file, filesize($this->data['lock']['queue']));
        fclose($file);

        // 文件列表
        $list = array_reduce(explode("\n", $text), function ($result, $v) {
            if ($v) $result[explode('.', $v)[0]] = $v;

            return $result;
        }, []);

        // 文件排序
        ksort($list);

        return $list;
    }

    /**
     * 合并文件
     *
     * @param array $queueList
     * @return bool
     */
    private function mergeFile(array $queueList): bool
    {
        // 文件锁
        $lock = fopen($this->data['lock']['lock'], 'c');

        // 获取到文件锁，则合并文件
        if (flock($lock, LOCK_EX | LOCK_NB)) {
            // 合并的文件路径
            $realFile = fopen($this->data['storage']['dir'] . $this->data['storage']['synthesis'] . $this->data['storage']['name'], 'w+');

            // 顺序写入文件
            while ($readFileName = array_shift($queueList)) fwrite($realFile, file_get_contents($this->data['storage']['dir'] . $this->data['storage']['temp'] . $readFileName));

            // 关闭锁
            flock($lock, LOCK_UN);
            fclose($lock);
            fclose($realFile);

            return true;
        }

        return false;
    }

    /**
     * 删除文件块记录
     *
     * @param array $list 文件块列表
     * @param string $key 文件key
     */
    private function deleteFileBlockRecording(array $list, string $key): void
    {
        $dir = $this->data['storage']['dir'] . $this->data['storage']['temp'];

        // 删除锁文件
        unlink($dir . 'file_list.txt');
        unlink($dir . 'file_list.lock');

        // 删除文件块
        while ($fileName = array_shift($list)) unlink($dir . $fileName);

        // 删除临时存储目录
        rmdir($dir);

        // 清除文件key
        Cache::delete($key);
    }
}